周五晚上十一点,自媒体博主小林还窝在工位上。这周他得交一条两分钟的科技科普视频,流程他闭着眼都能走:先写脚本,再手画分镜,然后钻进衣柜里录旁白(图个隔音),最后剪到凌晨两点。一个人活成了一条流水线——累,且慢。
隔壁市场部更头疼。产品要上新,想做个演示视频,找外包一问:两周起步,五位数报价,改一版还得排队。
小林盯着屏幕发愣时冒出一个念头:写脚本、画分镜、配配音、剪片子,这四件事 AI 其实都会。缺的不是能力,是一个把它们串起来的"人"。要是有个 Agent 能当导演,调度编剧、配音、剪辑这些工具,自己只管甩一句"做个两分钟的 Python 介绍视频"——那该多好?
这一章,我们就来造这个导演。
全书主线进度 :前面的项目都是"单一步骤"的任务——查订单、搜知识库、审查代码。现在我们要让 Agent 处理多步骤流水线 ——脚本→分镜→配音→合成,四步环环相扣,出错还要能重试或跳过。这是"单 Agent 越来越能干"的关键一步。
主讲能力 :多模态工具编排 + 流水线状态管理
业务场景 :用户输入视频主题,Agent 协调脚本创作、分镜设计、配音生成、视频合成全流程。
技术栈 :LangChain create_agent + 多个 Claude API 调用 + 流水线状态追踪
7.1 小林的苦衷
7.1.1 业务背景
短视频是当下最主流的内容形式。可一条视频背后,藏着四种手艺:策划脚本的编剧脑、设计分镜的美术眼、录制配音的播音嗓、合成画面的剪辑手。让一个人同时凑齐这四样,难;让四个人协同,慢。
AI 的妙处在于:这四种能力它都有,缺的只是把它们按顺序"排戏"的那个导演。而这,恰恰是 Agent 的本职。
7.1.2 用户故事
编号
作为
我想要
以便
US-1
自媒体创作者
输入主题就能得到一段完整视频
降低视频制作门槛
US-2
企业培训师
把培训大纲转成视频教程
批量生产培训内容
US-3
项目经理
能实时看到制作流水线的进度
知道哪一步出问题了
7.1.3 功能性需求
FR-1 脚本创作 :根据主题创作分场景脚本
FR-2 分镜生成 :提取每个场景的 AI 图片 prompt
FR-3 配音生成 :将旁白文本转为音频文件
FR-4 视频合成 :将画面和配音合成为最终视频
FR-5 进度追踪 :实时查看流水线各步骤状态
7.2 画个样子:它该长啥样
把这条流水线画出来,长这样:
%%{init: {'theme':'base','flowchart':{'useMaxWidth':true,'htmlLabels':true}}}%%
graph LR
Topic["用户输入主题"] --> Script["create_script 创作脚本"]
Script --> Board["generate_storyboard 生成分镜"]
Board --> Voice["generate_voiceover 生成配音"]
Voice --> Compose["compose_video 合成视频"]
Compose --> Output["最终视频文件"]
classDef s fill:#e3f2fd,stroke:#1976d2,stroke-width:2px,color:#0d47a1
classDef t fill:#e0f7fa,stroke:#00acc1,stroke-width:2px,color:#006064
class Topic,Output s
class Script,Board,Voice,Compose t
盯紧这张图——它不是一次大模型调用,而是四个工具在接力。Agent 站在中间当导演,喊一声"开拍",编剧工具先上场,写完剧本递给分镜工具;分镜画完递给配音工具;配音录完递给剪辑工具。哪一步卡壳了,导演还能喊停、重拍,或者干脆跳过这一条接着往下走。
金句:Agent 不是全能选手,而是最好的调度员。 它的本事不在样样精通,而在把对的人安排到对的工位上。
7.3 拆开看:怎么造出来
7.3.1 技术选型
技术点
选型
理由
Agent
create_agent
统一
脚本创作
Claude Sonnet(专用 prompt)
高品质文本生成
分镜提取
Claude Haiku(轻量任务)
结构化提取,成本低
配音
预留给真实 TTS API(如 ElevenLabs)
当前模拟实现
视频合成
预留给 FFmpeg 或其他合成工具
当前模拟实现
状态追踪
VideoPipelineState 类
每步独立追踪,可观测
💡 顿悟时刻 :很多人第一次做"AI 视频",本能反应是"找一个会生成视频的大模型,一句话出片"。真去试就会栽跟头——这条路又贵又不靠谱。原因很简单:视频生成本质是多个工具按序协作,不是一次大模型调用。 脚本要文本能力(Sonnet),分镜要结构化提取(Haiku 又快又省),配音要 TTS,合成要 FFmpeg。没有一个模型样样精通,但每个环节都有最合适的工具。Agent 的价值,就是当那个把工具排成一条龙的导演。
7.3.2 关键设计:流水线状态管理
导演得随时摸清剧组进度:哪场戏拍完了,哪场卡住了,哪场跳过了。这就是 VideoPipelineState 干的事——它是导演手里的场记板。
1 2 3 4 5 6 class VideoPipelineState : def update (self, step: str , status: str , output: str = "" ): self .steps[step] = {"status" : status, "output" : output, "timestamp" : ...} def summary (self ) -> str :
7.3.3 关键设计:模拟 vs 真实服务
先把丑话说前头:本项目的 generate_voiceover 和 compose_video 用的是模拟实现 ——生成的是文本占位文件,不是真能播放的 mp3、mp4。这是有意为之:真实的 TTS(如 ElevenLabs)和视频合成(如 FFmpeg)要额外的 API Key 和系统依赖,塞进一个"即装即跑"的教学项目里反而不友好。
但接口是预留好的:函数签名不动,把内部的 write_text 换成真实 API 调用,Agent 侧一行都不用改。换句话说,这是个骨架已经搭好、肌肉等你填 的教学项目。
7.4 动手写:三层架构完整代码
7.4.1 架构分层总览
这套导演系统分五层,各司其职:
层级
文件名
职责
核心类/函数
领域模型层
models.py
流水线状态、脚本、分镜等数据结构
PipelineState, StepResult, VideoScript, Storyboard
提示词层
prompts.py
脚本创作、分镜提取的提示词模板
build_script_prompt(), build_storyboard_prompt()
工具层
tools.py
各步骤的工具函数(脚本、分镜、配音、合成)
create_script(), generate_storyboard(), get_pipeline_status()
服务层
service.py
流水线编排、重试、降级策略
VideoProductionService
项目入口层
project.py
Agent 构建、项目注册
VideoGenerationProject
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 """P05 AI 视频生成 Agent - 领域模型层""" from __future__ import annotationsfrom dataclasses import dataclass, fieldfrom datetime import datetimefrom enum import Enumfrom typing import Any class PipelineStatus (str , Enum): """流水线步骤状态。""" PENDING = "pending" RUNNING = "running" SUCCESS = "success" FAILED = "failed" SKIPPED = "skipped" @dataclass class StepResult : """单个步骤的执行结果。""" step_name: str status: PipelineStatus output: str = "" error_message: str = "" start_time: str = field(default_factory=lambda : datetime.now().isoformat()) end_time: str = "" duration_seconds: float = 0.0 metadata: dict [str , Any ] = field(default_factory=dict ) @property def is_success (self ) -> bool : return self .status == PipelineStatus.SUCCESS @dataclass class VideoScript : """视频脚本。""" topic: str duration_minutes: int scenes: list [dict [str , Any ]] = field(default_factory=list ) voiceover_text: str = "" style: str = "professional" @dataclass class Storyboard : """分镜表。""" script_id: str = "" scenes: list [dict [str , Any ]] = field(default_factory=list ) def add_scene (self, scene_number: int , prompt: str , duration: int = 5 ) -> None : self .scenes.append({ "scene_number" : scene_number, "prompt" : prompt, "duration_seconds" : duration, }) @dataclass class PipelineState : """视频制作流水线状态。""" topic: str = "" steps: dict [str , StepResult] = field(default_factory=dict ) final_video_path: str = "" def update_step (self, step: str , status: PipelineStatus, output: str = "" , error: str = "" ) -> None : """更新步骤状态。""" if step not in self .steps: self .steps[step] = StepResult(step_name=step, status=status) self .steps[step].status = status self .steps[step].output = output self .steps[step].error_message = error def mark_step_end (self, step: str ) -> None : """标记步骤结束,计算耗时。""" if step in self .steps: self .steps[step].end_time = datetime.now().isoformat() start = datetime.fromisoformat(self .steps[step].start_time) end = datetime.fromisoformat(self .steps[step].end_time) self .steps[step].duration_seconds = (end - start).total_seconds() def get_progress_percent (self ) -> int : """获取总体进度百分比。""" if not self .steps: return 0 completed = sum (1 for s in self .steps.values() if s.status in (PipelineStatus.SUCCESS, PipelineStatus.SKIPPED)) return int (completed / len (self .steps) * 100 ) def summary_markdown (self ) -> str : """生成状态摘要(Markdown 格式)。""" status_emojis = { PipelineStatus.SUCCESS: "✅" , PipelineStatus.RUNNING: "🔄" , PipelineStatus.FAILED: "❌" , PipelineStatus.SKIPPED: "⏭️" , } step_names = { "script" : "脚本创作" , "storyboard" : "分镜生成" , "voiceover" : "配音生成" , "video_compose" : "视频合成" , } lines = ["## 🎬 视频制作流水线状态" , "" , f"**主题**: {self.topic or '未设置' } " , f"**进度**: {self.get_progress_percent()} %" , "" , "### 各步骤状态:" , "" ] for step_key, name in step_names.items(): step = self .steps.get(step_key) if step: emoji = status_emojis.get(step.status, "⏳" ) lines.append(f"{emoji} **{name} ** ({step.duration_seconds:.1 f} s)" ) if step.error_message: lines.append(f" 错误: {step.error_message} " ) else : lines.append(f"⏳ **{name} ** (未开始)" ) return "\n" .join(lines)
核心代码讲解 :
状态机设计 :每个步骤独立追踪,像剧组的场记单,哪场戏可以单独重拍、单独跳过,互不连坐
富进度信息 :不光记状态,还记执行时间、错误信息、输出结果——导演要的就是这种细颗粒度
Emoji 友好输出 :summary_markdown() 直接生成用户能看懂的进度报告,✅🔄❌ 一眼分明
幂等更新 :update_step() 可以反复调用,不会把已经记下的数据冲掉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 """P05 AI 视频生成 Agent - Prompt 层""" from __future__ import annotationsSCRIPT_PROMPT_TEMPLATE = """请为一段 {duration_minutes} 分钟的短视频创作详细脚本。 **主题**: {topic} ## 输出格式要求 请输出 Markdown 格式,严格遵守以下结构: # 视频脚本 ## 分场景脚本 ### 场景 1 **画面描述 (English)**: A visually striking opening shot... **旁白文本 (中文)**: 大家好,今天我们要聊的是... **建议时长**: 15 秒 ... ## 完整旁白(配音用) (将所有场景的旁白合并成一段连续的文本) ## 创作要求 1. **场景数量**: {scenes_count} 个场景(每个 10-20 秒) 2. **画面描述**: 用英文,适合作为 AI 图片生成的 prompt,包含景别、光线 3. **旁白文本**: 用中文,口语化、自然,适合朗读 4. **画面描述技巧**: 要包含主体描述、景别、光线、画质、风格 """ def build_script_prompt (topic: str , duration_minutes: int ) -> str : """构建脚本创作提示词。""" scenes_count = max (5 , int (duration_minutes * 4 )) return SCRIPT_PROMPT_TEMPLATE.format ( topic=topic, duration_minutes=duration_minutes, scenes_count=scenes_count, ) STORYBOARD_PROMPT_TEMPLATE = """请从以下视频脚本中提取每个场景的画面描述,转换为 AI 图片生成 prompt。 ## 脚本内容 {script_text} ## 输出要求 请输出 **纯 JSON 数组**,不要其他文字,不要 markdown 标记。格式如下: [ {{ "scene_number": 1, "prompt": "Wide shot of modern office, cinematic lighting, 4K, professional", "duration_seconds": 15 }} ] 每个 prompt 必须是英文,包含:景别、光线、画质、风格。 """ def build_storyboard_prompt (script_text: str ) -> str : """构建分镜提取提示词。""" return STORYBOARD_PROMPT_TEMPLATE.format (script_text=script_text[:4000 ])
核心代码讲解 :
格式契约明确 :每个 prompt 都把输出格式(Markdown / JSON)写得明明白白,降低解析失败率——给 AI 定规矩,比事后修格式省心
长度保护 :脚本超过 4000 字自动截断,防止 Token 溢出把后续步骤拖崩
结构化输出引导 :直接甩一段 JSON 样例给 AI 看,比干巴巴说"请输出 JSON"有效得多
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 """P05 AI 视频生成 Agent - 工具层""" from __future__ import annotationsimport jsonimport reimport timefrom pathlib import Pathfrom langchain.tools import toolfrom core.config import DATA_DIRfrom core.logging_conf import get_loggerfrom .models import PipelineState, PipelineStatusfrom .prompts import build_script_prompt, build_storyboard_promptlogger = get_logger("p05.video_generation.tools" ) VIDEO_OUTPUT_DIR = DATA_DIR / "videos" VIDEO_OUTPUT_DIR.mkdir(parents=True , exist_ok=True ) _pipeline = PipelineState() def _call_llm_for_script (prompt: str ) -> str : """调用 LLM 生成脚本。生产环境替换为真实的 Claude API 调用。""" try : from anthropic import Anthropic client = Anthropic() resp = client.messages.create( model="claude-sonnet-4-6" , max_tokens=4096 , messages=[{"role" : "user" , "content" : prompt}], ) return resp.content[0 ].text except ImportError: return "# 视频脚本\n\n## 场景 1\n**画面描述**: Test scene\n**旁白**: 测试旁白" @tool def create_script (topic: str , duration_minutes: int = 3 ) -> str : """根据主题创作视频脚本。""" logger.info("开始创建脚本: topic='%s'" , topic) _pipeline.topic = topic _pipeline.update_step("script" , PipelineStatus.RUNNING) try : prompt = build_script_prompt(topic, duration_minutes) script = _call_llm_for_script(prompt) _pipeline.update_step("script" , PipelineStatus.SUCCESS, output=f"脚本已生成,长度 {len (script)} 字符" ) _pipeline.mark_step_end("script" ) return script except Exception as e: _pipeline.update_step("script" , PipelineStatus.FAILED, error=str (e)) _pipeline.mark_step_end("script" ) return f"脚本创作失败: {e} " @tool def generate_storyboard (script: str ) -> str : """根据脚本生成分镜表。""" logger.info("开始生成分镜表" ) _pipeline.update_step("storyboard" , PipelineStatus.RUNNING) try : prompt = build_storyboard_prompt(script) storyboard_json = _call_llm_for_script(prompt) json_match = re.search(r'\[.*\]' , storyboard_json, re.DOTALL) if json_match: scenes = json.loads(json_match.group(0 )) _pipeline.update_step("storyboard" , PipelineStatus.SUCCESS, output=f"分镜表已生成,共 {len (scenes)} 个场景" ) else : _pipeline.update_step("storyboard" , PipelineStatus.SUCCESS, output="分镜表已生成(JSON 解析未确认)" ) _pipeline.mark_step_end("storyboard" ) return storyboard_json except Exception as e: _pipeline.update_step("storyboard" , PipelineStatus.FAILED, error=str (e)) _pipeline.mark_step_end("storyboard" ) return f"分镜生成失败: {e} " @tool def generate_voiceover (script_text: str , output_filename: str = "voiceover.mp3" ) -> str : """为脚本生成配音。生产环境替换为真实 TTS API。""" _pipeline.update_step("voiceover" , PipelineStatus.RUNNING) try : output_path = VIDEO_OUTPUT_DIR / output_filename output_path.write_text(f"[配音占位] 脚本长度: {len (script_text)} " , encoding="utf-8" ) _pipeline.update_step("voiceover" , PipelineStatus.SUCCESS, output=str (output_path)) _pipeline.mark_step_end("voiceover" ) return f"配音已生成并保存到: {output_path} " except Exception as e: _pipeline.update_step("voiceover" , PipelineStatus.FAILED, error=str (e)) return f"配音生成失败: {e} " @tool def compose_video (scenes_json: str , output_filename: str = "final_video.mp4" ) -> str : """将各场景画面和配音合成为最终视频。生产环境替换为 FFmpeg。""" _pipeline.update_step("video_compose" , PipelineStatus.RUNNING) try : output_path = VIDEO_OUTPUT_DIR / output_filename output_path.write_text(f"[视频占位] 场景数: {scenes_json.count('scene' )} " , encoding="utf-8" ) _pipeline.final_video_path = str (output_path) _pipeline.update_step("video_compose" , PipelineStatus.SUCCESS, output=str (output_path)) _pipeline.mark_step_end("video_compose" ) return f"视频已合成并保存到: {output_path} " except Exception as e: _pipeline.update_step("video_compose" , PipelineStatus.FAILED, error=str (e)) return f"视频合成失败: {e} " @tool def get_pipeline_status () -> str : """查看当前视频制作流水线的进度状态。""" return _pipeline.summary_markdown()
核心代码讲解 :
状态自动更新 :每个工具函数内部自己更新流水线状态,Agent 不用操心记场记——导演只管喊"开拍",进度单自动填
模拟 vs 真实接口 :工具签名稳定,内部实现可从"模拟"切到"真实 API",Agent 侧零改动
容错设计 :异常被捕获并记进流水线状态,不会让整条生产线因为一格胶片卡住而全盘崩
进度可查询 :get_pipeline_status() 让用户随时摸到脉搏
说明:上面这份清单为了聚焦主线做了精简。完整源码里分镜走的是单独的 _call_llm_for_storyboard,用的是 Claude Haiku(和 7.3.1 选型表对得上,又快又省);清单里复用 _call_llm_for_script 只是为演示省篇幅。
⚠️ 避坑:文件路径安全 。output_filename 是外部传入的参数,直接拼路径就有路径遍历风险——万一传进来一个 ../../etc/passwd,文件就写到外面去了。完整源码 tools.py 里用 _validate_output_filename 把关:禁止文件名包含 / 或 \,再用 Path.resolve() 校验最终路径必须落在 VIDEO_OUTPUT_DIR 内。这份精简清单没展示这段防护,落地时别忘了补上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 """P05 AI 视频生成 Agent - 服务层""" from __future__ import annotationsimport timefrom typing import Callable from core.logging_conf import get_loggerfrom .models import PipelineState, PipelineStatuslogger = get_logger("p05.video_generation.service" ) class VideoProductionService : """视频制作服务。编排流水线执行、重试、降级。""" def __init__ (self, max_retries: int = 2 , skip_on_failure: bool = True ): self .max_retries = max_retries self .skip_on_failure = skip_on_failure self .pipeline = PipelineState() def _execute_step (self, step_name: str , func: Callable , *args, **kwargs ) -> any : """执行单个步骤,包含重试逻辑。""" for attempt in range (self .max_retries + 1 ): try : logger.info("执行步骤 '%s',第 %d/%d 次尝试" , step_name, attempt + 1 , self .max_retries + 1 ) result = func(*args, **kwargs) if isinstance (result, str ) and "失败" in result: raise Exception(result) logger.info("步骤 '%s' 执行成功" , step_name) return result except Exception as e: logger.warning("步骤 '%s' 第 %d 次尝试失败: %s" , step_name, attempt + 1 , e) if attempt < self .max_retries: wait_seconds = 2 ** attempt logger.info("等待 %d 秒后重试..." , wait_seconds) time.sleep(wait_seconds) elif self .skip_on_failure: logger.warning("跳过步骤 '%s',继续后续流程" , step_name) return None else : raise return None def create_full_video (self, topic: str , duration_minutes: int = 2 ) -> PipelineState: """执行完整的视频制作流水线。""" from .tools import (create_script, generate_storyboard, generate_voiceover, compose_video) logger.info("开始完整视频制作: topic='%s'" , topic) self .pipeline.topic = topic script = self ._execute_step("script" , create_script.invoke, {"topic" : topic, "duration_minutes" : duration_minutes}) storyboard = None if script is not None : storyboard = self ._execute_step("storyboard" , generate_storyboard.invoke, {"script" : script}) if script is not None : self ._execute_step("voiceover" , generate_voiceover.invoke, {"script_text" : script[:2000 ]}) if storyboard is not None : self ._execute_step("video_compose" , compose_video.invoke, {"scenes_json" : storyboard}) logger.info("视频制作完成,进度: %d%%" , self .pipeline.get_progress_percent()) return self .pipeline def regenerate_step (self, step_name: str , **kwargs ) -> any : """重新执行某个步骤。""" from .tools import (create_script, generate_storyboard, generate_voiceover, compose_video) step_funcs = { "script" : create_script.invoke, "storyboard" : generate_storyboard.invoke, "voiceover" : generate_voiceover.invoke, "video_compose" : compose_video.invoke, } return self ._execute_step(step_name, step_funcs[step_name], kwargs)
核心代码讲解 :
自动重试 :_execute_step() 内置指数退避重试(1s→2s→4s),容忍临时故障,像导演给演员多几次 NG 的机会
失败降级 :单步失败可跳过继续,不浪费已成功步骤的资源——配音没录成,不代表前面写好的脚本要作废
编排可复用 :服务层编排不依赖 Agent,也能作为独立 API 供非 Agent 系统调用
进度钩子 :完整源码中 create_full_video 还接受 progress_callback 参数,可扩展为 WebSocket 实时推送进度
⚠️ 避坑:外部 API 调用降级 。脚本和分镜都靠 Claude API,网络抖动、限流、余额不足随时会让某一步炸掉。_execute_step 这套"重试 + 跳过"兜底,比"一次失败全盘崩溃"值钱得多——生产环境里,能带着伤继续跑的流水线,才是好流水线。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 """P05 AI 视频生成 Agent - 项目入口层""" from __future__ import annotationsfrom langchain.agents import create_agentfrom core import BaseProject, build_chat_model, registryfrom core.logging_conf import get_loggerfrom .prompts import build_system_promptfrom .tools import get_available_toolslogger = get_logger("p05.video_generation.project" ) class VideoGenerationProject (BaseProject ): """AI 视频生成 Agent 项目。""" id = "p05_video_generation" name = "AI 视频生成 Agent" description = "多模态工具编排:脚本→分镜→配音→视频合成" capabilities = ["多模态" , "流水线" , "LLM 编排" ] def build_agent (self ) -> any : """构建视频生成 Agent。""" model = build_chat_model() tools = get_available_tools() system_prompt = build_system_prompt() return create_agent(model=model, tools=tools, system_prompt=system_prompt) registry.register(VideoGenerationProject())
核心代码讲解 :
关注点分离 :工具层负责单步执行,服务层负责流水线编排,Agent 层负责人机交互理解——导演、剧组、场务各管一摊
多层 API :既支持完整 Agent 交互,也支持直接调用 Service 做自动化批量制作
可插拔架构 :工具内部实现可独立替换(模拟 TTS → 真实 TTS → 不同厂商 TTS),换个肌肉不伤骨架
7.5 跑一跑:它真的行吗
测试
验证
test_pipeline_state_tracking
状态追踪正常(✅🔄 图标正确)
test_create_script(集成)
脚本包含"场景"内容
test_basic_workflow(集成)
完整流水线能跑通
7.6 送上线:让它上班
同前——继承 BaseProject,自动接入统一 API。
7.7 回头看:学到了什么
能力
在本项目中的体现
多模态工具编排
脚本(文本) → 分镜(结构提取) → 配音(音频) → 合成(视频)
流水线状态
VideoPipelineState 每步独立追踪
失败降级
某步失败不影响已完成步骤
模拟服务模式
预留真实 API 接口,当前模拟实现
⚠️ 常见坑 :
脚本太长,分镜提取超时 — 已做 4000 字截断兜底
不查看流水线状态 — 用户不知道进度干着急。把状态查询工具化(get_pipeline_status)这件事,看着小,体验上天差地别
📌 项目五完成。 单 Agent 阶段过半——Agent 已经能从"一问一答"进化到"流水线编排"。下一章我们迈进 Multi-Agent 阶段 :让多个 Agent 组成团队,分工协作。
如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !